/**
 *
 */
package com.cmcc.awareness;

import java.util.ArrayList;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.Packet;

/**
 * @Date 2018年4月11日 上午9:59:23
 * @author Amish
 * 
 * 对应proto.dat中<START><END>之间的一个[TCP]或[UDP]获得
 * 
 * 中除端口号特征PORT和IP特征SERVER之外，其他特征均以{}括起，
 * 同一行中的多个{}括起的特征码之间是与的关系，不同行之间是或的关系；
 * PORT和SERVER的末尾字节标记为1时，与下面的单行特征是与的关系。
 * 
 * 注意注意：这里应该把PORT和SERVER的 与 或 关系参照分行/同行方式映射到代码List中，简化识别逻辑
 * 比如： PORT:4661|4662|4665|4672|1
         {b|0|0xe3}{b|1|0x9a}
      直接理解为：{PORT:4661|4662|4665|4672|1}{b|0|0xe3}{b|1|0x9a}
      再比如：
		SERVER:218.200.160.1/24|0
		SERVER:117.139.217.1/24|0
		SERVER:218.200.174.1/24|0
		SERVER:218.200.227.1/24|0
		SERVER:218.200.229.1/24|0
      直接理解为：
      	{SERVER:218.200.160.1/24|0}
      	{SERVER:117.139.217.1/24|0}
      	{SERVER:218.200.174.1/24|0}
      	{SERVER:218.200.227.1/24|0}
      	{SERVER:218.200.229.1/24|0}
 * eg 1
    [UDP]
    # 16635
    PORT:8000|8000|1
    {b|-1|0x03}{b|0|0x02}{b|3|0x00}{b|4|0x00}
    
    
   eg 2
	[UDP]
	# 6674
	{b|0|0xf1}{b|3|0x00}{b|4|0x00}{o|5|[0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb]}
	{b|0|0xf1}{b|3|0x00}{b|4|0x00}{o|5|[0xec,0xed,0xee,0xef,0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7]}
	{b|0|0xf1}{b|3|0x00}{b|4|0x00}{o|5|[0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff]}     
 * 
 */
public class ProtoDatProtocolFeatureCode {
	private static Logger logger = LogManager.getLogger(ProtoDatProtocolFeatureCode.class);
	
	public String appName; 

	public String protocolType;//TCP、UDP、HTTP、HTTPS、POP3S、SMTPS、IMAPS，以及HTTPDATA等
    public ArrayList<ArrayList<ProtoDatProtocolFeatureCodeElement>> protocolFeatureElements;//同一行的{},存在一个内层ArrayList中,不同行的{}存在protocolFetures中

	public ProtoDatProtocolFeatureCode(String appName){
		this.appName = appName;
		protocolFeatureElements = new ArrayList<ArrayList<ProtoDatProtocolFeatureCodeElement>>();
	}
    
    public boolean distinguish(String protocolType,jpcap.packet.Packet packet){
    	
    	if(!this.protocolType.equalsIgnoreCase(protocolType)){
    		return false;
    	}else{
    		for(ArrayList<ProtoDatProtocolFeatureCodeElement> al : protocolFeatureElements){
    			
    			if(al.isEmpty()){
    				return false;
    			}
    			
    			boolean lineResult = true;
    			for(ProtoDatProtocolFeatureCodeElement element : al){
    				lineResult = lineResult && element.distinguish(protocolType,packet);//TODO 发现一个false就可以return了
    			}
    			
    			if(lineResult){//如果某一行匹配识别，直接返回true
    				//only for test
    				System.out.print("Get It -- <"+this.appName+">"+"<"+this.protocolType+"> : ");
    				for(ProtoDatProtocolFeatureCodeElement element : al){
        				System.out.print(element.toString());
        			}
    				System.out.print("\n");
    				return lineResult;
    			}
    		}
    	}
    	return false;
    }
    
    public boolean distinguish(String protocolType,Packet packet){
    	
    	if(!this.protocolType.equalsIgnoreCase(protocolType)){
    		return false;
    	}else{
    		for(ArrayList<ProtoDatProtocolFeatureCodeElement> al : protocolFeatureElements){
    			
    			if(al.isEmpty()){
    				return false;
    			}
    			
    			boolean lineResult = true;
    			for(ProtoDatProtocolFeatureCodeElement element : al){
    				lineResult = lineResult && element.distinguish(protocolType,packet);//TODO 发现一个false就可以return了
    			}
    			
    			if(lineResult){//如果某一行匹配识别，直接返回true
    				//only for test
    				System.out.print("Get It -- <"+this.appName+">"+"<"+this.protocolType+"> : ");
    				for(ProtoDatProtocolFeatureCodeElement element : al){
        				System.out.print(element.toString());
        			}
    				System.out.print("\n");
    				return lineResult;
    			}
    		}
    	}
    	return false;
    }
    
    
    /**
     * 
     * @param alTempSub
     */
    public void parseProtoDatProtocolFetureCode(ArrayList<String> alTempSub){
    	ArrayList<ProtoDatProtocolFeatureCodeElement> al = null;
    	for(String str : alTempSub){
    		
    		if(str.endsWith("0")){
    			al = new ArrayList<ProtoDatProtocolFeatureCodeElement>();
    			if(str.startsWith("SERVER")){   				
    				ProtoDatProtocolFeatureCodeElement_SERVER element = new ProtoDatProtocolFeatureCodeElement_SERVER(this.appName,this.protocolType);
    				element.parseSelf(str);
    				al.add(element);
    				
    			}else if(str.startsWith("PORT")){

    				//System.out.println(str);
    				ProtoDatProtocolFeatureCodeElement_PORT element = new ProtoDatProtocolFeatureCodeElement_PORT(this.appName,this.protocolType);
    				element.parseSelf(str);
    				al.add(element);	
    				
    			}
    			protocolFeatureElements.add(al);
    			al = null;//因为0是或的关系，所以此处需要将al置null。

    		}else if(str.endsWith("1")){
    			if(al == null){//第一行
    				al = new ArrayList<ProtoDatProtocolFeatureCodeElement>();
    			}
                if(str.startsWith("SERVER")){
                	ProtoDatProtocolFeatureCodeElement_SERVER element = new ProtoDatProtocolFeatureCodeElement_SERVER(this.appName,this.protocolType);
    				element.parseSelf(str);
    				al.add(element);
    				
    			}else if(str.startsWith("PORT")){
    				//System.out.println(str);
    				ProtoDatProtocolFeatureCodeElement_PORT element = new ProtoDatProtocolFeatureCodeElement_PORT(this.appName,this.protocolType);
    				element.parseSelf(str);
    				al.add(element);	
    			}
                //因为1是与的关系，所以此处[不要]将al置null；
    			
    		}else{
    			if(al == null){//第一行
    				al = new ArrayList<ProtoDatProtocolFeatureCodeElement>();
    			}
    			
    			if(str.contains("}{")){//一行有多个{}的情况
    				//System.out.println(str);
    				String[] protoDatProtocolFeatureCodeElements = str.split("\\}\\{");
        			for(String element : protoDatProtocolFeatureCodeElements){
        				
        				this.parseElementDispatcher(element, al);

        			}
    			}else{//一行只有一个{}的情况
    				//ystem.out.println(str);
    				this.parseElementDispatcher(str, al);
    			}
    			protocolFeatureElements.add(al);
    			al = null;//解析完一行，所以此处需要将al置null。
    		}
    	}
		
	}
    
    public void parseElementDispatcher(String element,ArrayList<ProtoDatProtocolFeatureCodeElement> al){
    	element = element.replaceAll("\\{", "");
    	element = element.replaceAll("\\}", "");
		switch(element.substring(0,1)){
		case "l":
			ProtoDatProtocolFeatureCodeElement_l element_l = new ProtoDatProtocolFeatureCodeElement_l(this.appName,this.protocolType);
			element_l.parseSelf(element);
			al.add(element_l);
			break;
		case "d":
			ProtoDatProtocolFeatureCodeElement_d element_d = new ProtoDatProtocolFeatureCodeElement_d(this.appName,this.protocolType);
			element_d.parseSelf(element);
			al.add(element_d);
			break;
		case "b":
			ProtoDatProtocolFeatureCodeElement_b element_b = new ProtoDatProtocolFeatureCodeElement_b(this.appName,this.protocolType);
			element_b.parseSelf(element);
			al.add(element_b);
			break;
		case "o":
			ProtoDatProtocolFeatureCodeElement_o element_o = new ProtoDatProtocolFeatureCodeElement_o(this.appName,this.protocolType);
			element_o.parseSelf(element);
			al.add(element_o);
			break;
		case "s":
			ProtoDatProtocolFeatureCodeElement_s element_s =  new ProtoDatProtocolFeatureCodeElement_s(this.appName,this.protocolType);
			element_s.parseSelf(element);
			al.add(element_s);
			break;
		case "c":
			ProtoDatProtocolFeatureCodeElement_c element_c = new ProtoDatProtocolFeatureCodeElement_c(this.appName,this.protocolType);
			element_c.parseSelf(element);
			al.add(element_c);
			break;
		case "e":
			//do nothing
			break;
		case "x":
			ProtoDatProtocolFeatureCodeElement_x element_x = new ProtoDatProtocolFeatureCodeElement_x(this.appName,this.protocolType);
			element_x.parseSelf(element);
			al.add(element_x);
			break;
		default:
			break;
		
		}
    }
    
    public String toString(){
    	StringBuilder sb = new StringBuilder();
    	sb.append("["+this.protocolType+"]\n");
		for(ArrayList<ProtoDatProtocolFeatureCodeElement> al : protocolFeatureElements){
			for(ProtoDatProtocolFeatureCodeElement protoDatProtocolFeatureCodeElement  : al ){
				sb.append(protoDatProtocolFeatureCodeElement.toString());
			}
			sb.append("\n");
		}
		return sb.toString();
    }
    
}
